package com.seleniti.enpareja.controller.ui.view;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.ioc.commons.flowcontrol.actioncontroller.ActionController;
import org.ioc.commons.flowcontrol.actioncontroller.ActionHandler;
import org.ioc.commons.flowcontrol.common.impl.ReceiverAdapter;
import org.ioc.commons.flowcontrol.eventbus.EventBus;
import org.ioc.commons.flowcontrol.logincontroller.IsLoggedUserInfoConsumer;
import org.ioc.commons.flowcontrol.operationmanager.OperationManager;
import org.ioc.commons.flowcontrol.operationmanager.impl.OperationHandlerFactory;
import org.ioc.commons.model.globaldata.GlobalDataController.Consumer;
import org.ioc.commons.model.globaldata.IsGlobalDataConsumer;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;

import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.model.integration.database.domain.Food;
import com.seleniti.enpareja.controller.model.integration.database.domain.NotificationType;
import com.seleniti.enpareja.controller.model.integration.service.MealService;
import com.seleniti.enpareja.controller.ui.base.EnParejaViewBasePresenter;
import com.seleniti.enpareja.controller.ui.main.AppShell;
import com.seleniti.enpareja.controller.ui.view.MealsView.Action;
import com.seleniti.enpareja.controller.ui.view.MealsView.ActionMode;
import com.seleniti.enpareja.controller.ui.view.MealsView.Board;
import com.seleniti.enpareja.controller.ui.view.MealsView.DoubleSelection;
import com.seleniti.enpareja.controller.ui.view.MealsView.Event;
import com.seleniti.enpareja.controller.ui.view.MealsView.Operation;

/**
 * Presenter for the Login view
 * 
 * @author Jesús Lunar Pérez
 * 
 */
public class MealsViewPresenter extends
		EnParejaViewBasePresenter<MealsView, MealsView.Event, MealsView.Operation, MealsView.Action> implements
		MealsView.Presenter, IsLoggedUserInfoConsumer<LoggedUserInfo>, IsGlobalDataConsumer<GlobalData> {

	private static final FormatterLogger logger = FormatterLogger.getLogger(MealsViewPresenter.class);
	private MealService mealservice;
	private Consumer<GlobalData> consumer;

	public MealsViewPresenter(MealsView display, EventBus<Event> eventbus,
			OperationManager<Operation> operationManager, ActionController<Action> actionController) {
		super(display, eventbus, operationManager, actionController);
		this.consumer = this.globalDataController.registerGlobalDataConsumer(this);
	}

	@Override
	public void onInitDisplay() {
		super.onInitDisplay();
		this.mealservice = serviceManager.serviceFactory().mealService();
		if (display.getActionMode() == null) {
			display.setActionMode(ActionMode.MODIFY_ELEMENT);
		}
		if (display.boardShown().getValue() == null) {
			display.boardShown().setValue(Board.MAIN_LIST);
		}

		this.loginController.requiresLoggedUserInfoIfDisplayActive(this);
		this.consumer.requires(GlobalData.UserAgent);

	}

	@Override
	public void onDeactive() {
		super.onDeactive();

		shell.switchButtonVisibility().setVisible(false);
		shell.select2InfoVisibility().setVisible(false);
	}

	@Override
	protected void bindComponents() {
		super.bindComponents();
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(display));
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(shell.globalLoader()));

		shell.actionController().bindAction(AppShell.Action.DeleteNow, new ActionHandler<AppShell.Action>() {

			@Override
			public void handleAction(Object sender, com.seleniti.enpareja.controller.ui.main.AppShell.Action action,
					Object... params) {
				deleteSelected();
			}
		});

		shell.actionController().bindAction(AppShell.Action.SwitchElementsNow, new ActionHandler<AppShell.Action>() {

			@Override
			public void handleAction(Object sender, com.seleniti.enpareja.controller.ui.main.AppShell.Action action,
					Object... params) {
				switchMeals();
			}
		});
	}

	protected void deleteSelected() {
		if (display.getActionMode() == ActionMode.DELETE_ELEMENT) {
			Set<Food> selection = display.getSingleSelection();

			this.mealservice.deleteAll(selection).performing(Operation.Deleting, operationManager).caller(this)
					.to(new ReceiverAdapter<Void>() {

						@Override
						public void success(Void result) {
							refreshData();
						}
					});

			display.setActionMode(ActionMode.MODIFY_ELEMENT);
		}

	}

	@SuppressWarnings("deprecation")
	@Override
	public void onActive() {
		super.onActive();

		Date oneDayBefore = new Date();
		Date tenDaysLater = new Date();
		oneDayBefore.setDate(oneDayBefore.getDate() - 1);
		tenDaysLater.setDate(tenDaysLater.getDate() + 10);

		display.filterFromDate().setValue(oneDayBefore);
		display.filterToDate().setValue(tenDaysLater);

		// refreshData();

		shell.refreshButtonVisibility().setVisible(true);

		LoggedUserInfo lui = this.loginController.getLoggedUserInfo();

		display.setEnabled(lui != null && lui.getCoupleName() != null && !lui.getCoupleName().isEmpty());

		shell.nonAssociatedPanelVisibility()
				.setVisible(
						lui != null
								&& lui.isLogged()
								&& (lui.getCoupleName() == null || lui.getCoupleName().isEmpty() || lui.getCoupleType() == CoupleType.TESTING_MODE_COUPLE));

		shell.firstStepsVisibility().setVisible(
				shell.nonAssociatedPanelVisibility().isVisible()
						&& lui.getCoupleType() != CoupleType.TESTING_MODE_COUPLE);
		shell.nonAssociatedButtonVisibility().setVisible(!shell.firstStepsVisibility().isVisible());

		ActionMode mode = display.getActionMode();

		if (mode == ActionMode.SWITCH_MEALS) {
			refreshSwitchMealsButtonsVisibility();
		}

		this.consumer.ensureRequirements();
	}

	@Override
	public void onRequiredGlobalDataReady() {
		showGoBackButtonIfRequired();
	}

	@Override
	public void onRequiredGlobalDataNotReadyYet() {
		/*
		 * Nothing to do
		 */
	}

	@Override
	public void onRequiredGlobalDataUpdated(GlobalData globalDataUpdated) {
		showGoBackButtonIfRequired();
	}

	@Override
	public void onRefreshData() {
		refreshData();
	}

	@Override
	protected void refreshData() {
		// super.refreshCouple(new ReceiverAdapter<Couple>() {
		//
		// @Override
		// public void success(Couple result) {
		// }
		// }, Operation.GettingData, operationManager);

		if (!this.operationManager.isOpPending(Operation.GettingData)) {
			Date fromDate = display.filterFromDate().getValue();
			Date toDate = display.filterToDate().getValue();
			if (fromDate != null && toDate != null) {
				this.mealservice.getList(fromDate, toDate, true).performing(Operation.GettingData, operationManager)
						.caller(this).to(new ReceiverAdapter<List<Food>>() {

							@Override
							public void success(List<Food> result) {
								display.foodsList().clear();
								if (result != null) {
									logger.debug("Dates got: {0}",
											Format.collectionToString(result, new Format.StringFormatter<Food>() {

												@Override
												public String asString(Food obj) {
													return obj != null ? String.valueOf(obj.getDate()) : null;
												}
											}));

									display.foodsList().addAll(result);
								}
							}
						});
			} else {
				display.foodsList().clear();
			}
		} else {
			logger.info("Already getting data. We do not call it again.");
		}
	}

	@Override
	public void onLoggedUserInfoDataReady() {
		LoggedUserInfo loggedUserInfo = this.loginController.getLoggedUserInfo();

		if (!Objects.equals(loggedUserInfo, display.loggedUserInfo().getValue())) {
			this.display.loggedUserInfo().setValue(loggedUserInfo);

			refreshData();
		}
	}

	@Override
	public void onLoggedUserInfoDataUpdated(LoggedUserInfo loggedUserInfo) {
	}

	@Override
	public void onLoggedUserInfoNotReadyYet() {
		/*
		 * Nothing to do
		 */
	}

	@Override
	public void onActionModeChanged() {
		ActionMode mode = display.getActionMode();

		logger.info("Action mode changed: {0}", mode);

		switch (mode) {
		case DELETE_ELEMENT:
			this.shell.deleteButtonVisibility().setVisible(true);
			this.shell.select2InfoVisibility().setVisible(false);
			this.shell.switchButtonVisibility().setVisible(false);
			break;
		case SWITCH_MEALS:
			this.shell.deleteButtonVisibility().setVisible(false);
			refreshSwitchMealsButtonsVisibility();
			break;
		default:
			this.shell.deleteButtonVisibility().setVisible(false);
			this.shell.select2InfoVisibility().setVisible(false);
			this.shell.switchButtonVisibility().setVisible(false);
		}

	}

	@Override
	public void onSingleSelectionChanged() {
		ActionMode mode = display.getActionMode();

		logger.info("Selection changed on mode {0}", mode);

		if (mode == ActionMode.MODIFY_ELEMENT) {
			Set<Food> selection = display.getSingleSelection();

			if (!selection.isEmpty()) {
				final Food single = selection.iterator().next();
				display.showModifyForm(single);
			}
		}
	}

	@Override
	public void onDoubleSelectionChanged() {
		ActionMode mode = display.getActionMode();

		logger.info("Selection changed on mode {0}", mode);

		if (mode == ActionMode.SWITCH_MEALS) {
			refreshSwitchMealsButtonsVisibility();
		}
	}

	private void refreshSwitchMealsButtonsVisibility() {
		Set<DoubleSelection> selection = display.getDoubleSelection();

		shell.switchButtonVisibility().setVisible(selection.size() == 2);
		shell.select2InfoVisibility().setVisible(selection.size() != 2);
	}

	protected void switchMeals() {
		display.hideModificationForm();

		Set<DoubleSelection> selection = display.getDoubleSelection();

		if (selection.size() == 2) {
			Iterator<DoubleSelection> it = selection.iterator();
			DoubleSelection sel1 = it.next();
			DoubleSelection sel2 = it.next();
			mealservice.switchMeals(sel1.getElement(), sel1.isDinner(), sel2.getElement(), sel2.isDinner())
					.caller(this).performing(Operation.Saving, operationManager).to(new ReceiverAdapter<Void>() {

						@Override
						public void success(Void result) {
							refreshData();

							serviceManager.serviceFactory().notificationService()
									.notifyLoggedUserCouple(NotificationType.MEALS_CHANGED)
									.caller(MealsViewPresenter.this);
						}
					});

		}

	}

	@Override
	public void onMealModified() {
		Date date = display.date().getValue();
		if (date != null) {

			logger.info("Creating or replace meal for {0}", date);

			display.hideModificationForm();

			String lunch = display.lunch().getText();
			String dinner = display.dinner().getText();
			this.mealservice.createOrReplace(date, lunch, dinner).performing(Operation.Saving, operationManager)
					.caller(this).to(new ReceiverAdapter<Void>() {

						@Override
						public void success(Void result) {
							refreshData();

							serviceManager.serviceFactory().notificationService()
									.notifyLoggedUserCouple(NotificationType.MEALS_CHANGED)
									.caller(MealsViewPresenter.this);
						}
					});

		} else {
			messageBoxFactory.alert("Decidir comida", "Debes poner una fecha");
		}
	}

	@Override
	public void onDateFilterChanged() {
		refreshData();
	}

	@Override
	public boolean onGoBoardBack() {
		if (this.display.boardShown().getValue() != Board.MAIN_LIST) {
			this.display.boardShown().setValue(Board.MAIN_LIST);
			return true;
		}

		return false;
	}
}
